/**
 * 农历（阴历）万年历
 * LunarCalendar
 * vervison : v0.1.4
 * Github : https://github.com/zzyss86/LunarCalendar
 * HomePage : http://www.tuijs.com/
 * Author : JasonZhou
 * Email : zzyss86@qq.com
 */

import fixedDate from "./fixedDate";

// ---------------------------------------------
// 于 2021.01.25 添加
// 以上为原代码注释
// 原代码地址: https://github.com/zzyss86/LunarCalendar/blob/master/lib/LunarCalendar.js
// 感谢此库作者的代码贡献
// 此项目中，对原代码进行了一些改动，以适应此项目的编码规范
// ---------------------------------------------

const extend = function (o, c) {
  if (o && c && typeof c == "object") {
    for (let p in c) {
      o[p] = c[p];
    }
  }
  return o;
};

const creatLenArr = function (year, month, len, start) {
  const arr = [];
  start = start || 0;
  if (len < 1) return arr;
  let k = start;
  for (let i = 0; i < len; i++) {
    arr.push({ year: year, month: month, day: k });
    k++;
  }
  return arr;
};

//错误码列表
const errorCode = {
  100: "输入的年份超过了可查询范围，仅支持1891至2100年",
  101: "参数输入错误，请查阅文档",
};

//某年相同计算进行cache，以加速计算速度
let cache = null;
//cache管理工具

const cacheUtil = {
  current: "",
  setCurrent: function (year) {
    if (this.current !== year) {
      this.current = year;
      this.clear();
    }
  },
  set: function (key, value) {
    if (!cache) cache = {};
    cache[key] = value;
    return cache[key];
  },
  get: function (key) {
    if (!cache) cache = {};
    return cache[key];
  },
  clear: function () {
    cache = null;
  },
};

const formatDayD4 = function (month, day) {
  month = month + 1;
  month = month < 10 ? "0" + month : month;
  day = day < 10 ? "0" + day : day;
  return "d" + month + day;
};

const minYear = 1890; //最小年限
const maxYear = 2100; //最大年限

const DATA = {
  heavenlyStems: ["甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸"], //天干
  earthlyBranches: [
    "子",
    "丑",
    "寅",
    "卯",
    "辰",
    "巳",
    "午",
    "未",
    "申",
    "酉",
    "戌",
    "亥",
  ], //地支
  zodiac: [
    "鼠",
    "牛",
    "虎",
    "兔",
    "龙",
    "蛇",
    "马",
    "羊",
    "猴",
    "鸡",
    "狗",
    "猪",
  ], //对应地支十二生肖
  solarTerm: [
    "小寒",
    "大寒",
    "立春",
    "雨水",
    "惊蛰",
    "春分",
    "清明",
    "谷雨",
    "立夏",
    "小满",
    "芒种",
    "夏至",
    "小暑",
    "大暑",
    "立秋",
    "处暑",
    "白露",
    "秋分",
    "寒露",
    "霜降",
    "立冬",
    "小雪",
    "大雪",
    "冬至",
  ], //二十四节气
  monthCn: [
    "正",
    "二",
    "三",
    "四",
    "五",
    "六",
    "七",
    "八",
    "九",
    "十",
    "冬",
    "腊",
  ],
  dateCn: [
    "初一",
    "初二",
    "初三",
    "初四",
    "初五",
    "初六",
    "初七",
    "初八",
    "初九",
    "初十",
    "十一",
    "十二",
    "十三",
    "十四",
    "十五",
    "十六",
    "十七",
    "十八",
    "十九",
    "二十",
    "廿一",
    "廿二",
    "廿三",
    "廿四",
    "廿五",
    "廿六",
    "廿七",
    "廿八",
    "廿九",
    "三十",
    "卅一",
  ],
};

//中国节日放假安排，外部设置，0无特殊安排，1工作，2放假
const worktime = {};

//公历节日
const solarFestival = {
  d0101: "元旦节",
  d0202: "世界湿地日",
  d0210: "国际气象节",
  d0214: "情人节",
  d0301: "国际海豹日",
  d0303: "全国爱耳日",
  d0305: "学雷锋纪念日",
  d0308: "妇女节",
  d0312: "植树节 孙中山逝世纪念日",
  d0314: "国际警察日",
  d0315: "消费者权益日",
  d0317: "中国国医节 国际航海日",
  d0321: "世界森林日 消除种族歧视国际日 世界儿歌日",
  d0322: "世界水日",
  d0323: "世界气象日",
  d0324: "世界防治结核病日",
  d0325: "全国中小学生安全教育日",
  d0330: "巴勒斯坦国土日",
  d0401: "愚人节 全国爱国卫生运动月(四月) 税收宣传月(四月)",
  d0407: "世界卫生日",
  d0422: "世界地球日",
  d0423: "世界图书和版权日",
  d0424: "亚非新闻工作者日",
  d0501: "劳动节",
  d0504: "青年节",
  d0505: "碘缺乏病防治日",
  d0508: "世界红十字日",
  d0512: "国际护士节",
  d0515: "国际家庭日",
  d0517: "世界电信日",
  d0518: "国际博物馆日",
  d0520: "全国学生营养日",
  d0522: "国际生物多样性日",
  d0523: "国际牛奶日",
  d0531: "世界无烟日",
  d0601: "国际儿童节",
  d0605: "世界环境日",
  d0606: "全国爱眼日",
  d0617: "防治荒漠化和干旱日",
  d0623: "国际奥林匹克日",
  d0625: "全国土地日",
  d0626: "国际禁毒日",
  d0701: "香港回归纪念日 中共诞辰 世界建筑日",
  d0702: "国际体育记者日",
  d0707: "抗日战争纪念日",
  d0711: "世界人口日",
  d0730: "非洲妇女日",
  d0801: "建军节",
  d0808: "中国男子节(爸爸节)",
  d0815: "抗日战争胜利纪念",
  d0908: "国际扫盲日 国际新闻工作者日",
  d0909: "毛泽东逝世纪念",
  d0910: "中国教师节",
  d0914: "世界清洁地球日",
  d0916: "国际臭氧层保护日",
  d0918: "九一八事变纪念日",
  d0920: "国际爱牙日",
  d0927: "世界旅游日",
  d0928: "孔子诞辰",
  d1001: "国庆节 世界音乐日 国际老人节",
  d1002: "国际和平与民主自由斗争日",
  d1004: "世界动物日",
  d1006: "老人节",
  d1008: "全国高血压日 世界视觉日",
  d1009: "世界邮政日 万国邮联日",
  d1010: "辛亥革命纪念日 世界精神卫生日",
  d1013: "世界保健日 国际教师节",
  d1014: "世界标准日",
  d1015: "国际盲人节(白手杖节)",
  d1016: "世界粮食日",
  d1017: "世界消除贫困日",
  d1022: "世界传统医药日",
  d1024: "联合国日 世界发展信息日",
  d1031: "世界勤俭日",
  d1107: "十月社会主义革命纪念日",
  d1108: "中国记者日",
  d1109: "全国消防安全宣传教育日",
  d1110: "世界青年节",
  d1111: "国际科学与和平周(本日所属的一周)",
  d1112: "孙中山诞辰纪念日",
  d1114: "世界糖尿病日",
  d1117: "国际大学生节 世界学生节",
  d1121: "世界问候日 世界电视日",
  d1129: "国际声援巴勒斯坦人民国际日",
  d1201: "世界艾滋病日",
  d1203: "世界残疾人日",
  d1205: "国际经济和社会发展志愿人员日",
  d1208: "国际儿童电视日",
  d1209: "世界足球日",
  d1210: "世界人权日",
  d1212: "西安事变纪念日",
  d1213: "南京大屠杀(1937年)纪念日！紧记血泪史！",
  d1220: "澳门回归纪念",
  d1221: "国际篮球日",
  d1224: "平安夜",
  d1225: "圣诞节",
  d1226: "毛泽东诞辰纪念",
};

//农历节日
const lunarFestival = {
  d0101: "春节",
  d0115: "元宵节",
  d0202: "龙抬头",
  d0323: "妈祖生辰",
  d0505: "端午节",
  d0707: "七夕",
  d0715: "中元节",
  d0815: "中秋节",
  d0909: "重阳节",
  d1015: "下元节",
  d1208: "腊八节",
  d1223: "小年",
  d0100: "除夕",
};

/**
 * 1890 - 2100 年的农历数据
 * 数据格式：[0,2,9,21936]
 * [闰月所在月，0为没有闰月; *正月初一对应公历月; *正月初一对应公历日; *农历每月的天数的数组（需转换为二进制,得到每月大小，0=小月(29日),1=大月(30日)）;]
 */
const lunarInfo = [
  [2, 1, 21, 22184],
  [0, 2, 9, 21936],
  [6, 1, 30, 9656],
  [0, 2, 17, 9584],
  [0, 2, 6, 21168],
  [5, 1, 26, 43344],
  [0, 2, 13, 59728],
  [0, 2, 2, 27296],
  [3, 1, 22, 44368],
  [0, 2, 10, 43856],
  [8, 1, 30, 19304],
  [0, 2, 19, 19168],
  [0, 2, 8, 42352],
  [5, 1, 29, 21096],
  [0, 2, 16, 53856],
  [0, 2, 4, 55632],
  [4, 1, 25, 27304],
  [0, 2, 13, 22176],
  [0, 2, 2, 39632],
  [2, 1, 22, 19176],
  [0, 2, 10, 19168],
  [6, 1, 30, 42200],
  [0, 2, 18, 42192],
  [0, 2, 6, 53840],
  [5, 1, 26, 54568],
  [0, 2, 14, 46400],
  [0, 2, 3, 54944],
  [2, 1, 23, 38608],
  [0, 2, 11, 38320],
  [7, 2, 1, 18872],
  [0, 2, 20, 18800],
  [0, 2, 8, 42160],
  [5, 1, 28, 45656],
  [0, 2, 16, 27216],
  [0, 2, 5, 27968],
  [4, 1, 24, 44456],
  [0, 2, 13, 11104],
  [0, 2, 2, 38256],
  [2, 1, 23, 18808],
  [0, 2, 10, 18800],
  [6, 1, 30, 25776],
  [0, 2, 17, 54432],
  [0, 2, 6, 59984],
  [5, 1, 26, 27976],
  [0, 2, 14, 23248],
  [0, 2, 4, 11104],
  [3, 1, 24, 37744],
  [0, 2, 11, 37600],
  [7, 1, 31, 51560],
  [0, 2, 19, 51536],
  [0, 2, 8, 54432],
  [6, 1, 27, 55888],
  [0, 2, 15, 46416],
  [0, 2, 5, 22176],
  [4, 1, 25, 43736],
  [0, 2, 13, 9680],
  [0, 2, 2, 37584],
  [2, 1, 22, 51544],
  [0, 2, 10, 43344],
  [7, 1, 29, 46248],
  [0, 2, 17, 27808],
  [0, 2, 6, 46416],
  [5, 1, 27, 21928],
  [0, 2, 14, 19872],
  [0, 2, 3, 42416],
  [3, 1, 24, 21176],
  [0, 2, 12, 21168],
  [8, 1, 31, 43344],
  [0, 2, 18, 59728],
  [0, 2, 8, 27296],
  [6, 1, 28, 44368],
  [0, 2, 15, 43856],
  [0, 2, 5, 19296],
  [4, 1, 25, 42352],
  [0, 2, 13, 42352],
  [0, 2, 2, 21088],
  [3, 1, 21, 59696],
  [0, 2, 9, 55632],
  [7, 1, 30, 23208],
  [0, 2, 17, 22176],
  [0, 2, 6, 38608],
  [5, 1, 27, 19176],
  [0, 2, 15, 19152],
  [0, 2, 3, 42192],
  [4, 1, 23, 53864],
  [0, 2, 11, 53840],
  [8, 1, 31, 54568],
  [0, 2, 18, 46400],
  [0, 2, 7, 46752],
  [6, 1, 28, 38608],
  [0, 2, 16, 38320],
  [0, 2, 5, 18864],
  [4, 1, 25, 42168],
  [0, 2, 13, 42160],
  [10, 2, 2, 45656],
  [0, 2, 20, 27216],
  [0, 2, 9, 27968],
  [6, 1, 29, 44448],
  [0, 2, 17, 43872],
  [0, 2, 6, 38256],
  [5, 1, 27, 18808],
  [0, 2, 15, 18800],
  [0, 2, 4, 25776],
  [3, 1, 23, 27216],
  [0, 2, 10, 59984],
  [8, 1, 31, 27432],
  [0, 2, 19, 23232],
  [0, 2, 7, 43872],
  [5, 1, 28, 37736],
  [0, 2, 16, 37600],
  [0, 2, 5, 51552],
  [4, 1, 24, 54440],
  [0, 2, 12, 54432],
  [0, 2, 1, 55888],
  [2, 1, 22, 23208],
  [0, 2, 9, 22176],
  [7, 1, 29, 43736],
  [0, 2, 18, 9680],
  [0, 2, 7, 37584],
  [5, 1, 26, 51544],
  [0, 2, 14, 43344],
  [0, 2, 3, 46240],
  [4, 1, 23, 46416],
  [0, 2, 10, 44368],
  [9, 1, 31, 21928],
  [0, 2, 19, 19360],
  [0, 2, 8, 42416],
  [6, 1, 28, 21176],
  [0, 2, 16, 21168],
  [0, 2, 5, 43312],
  [4, 1, 25, 29864],
  [0, 2, 12, 27296],
  [0, 2, 1, 44368],
  [2, 1, 22, 19880],
  [0, 2, 10, 19296],
  [6, 1, 29, 42352],
  [0, 2, 17, 42208],
  [0, 2, 6, 53856],
  [5, 1, 26, 59696],
  [0, 2, 13, 54576],
  [0, 2, 3, 23200],
  [3, 1, 23, 27472],
  [0, 2, 11, 38608],
  [11, 1, 31, 19176],
  [0, 2, 19, 19152],
  [0, 2, 8, 42192],
  [6, 1, 28, 53848],
  [0, 2, 15, 53840],
  [0, 2, 4, 54560],
  [5, 1, 24, 55968],
  [0, 2, 12, 46496],
  [0, 2, 1, 22224],
  [2, 1, 22, 19160],
  [0, 2, 10, 18864],
  [7, 1, 30, 42168],
  [0, 2, 17, 42160],
  [0, 2, 6, 43600],
  [5, 1, 26, 46376],
  [0, 2, 14, 27936],
  [0, 2, 2, 44448],
  [3, 1, 23, 21936],
  [0, 2, 11, 37744],
  [8, 2, 1, 18808],
  [0, 2, 19, 18800],
  [0, 2, 8, 25776],
  [6, 1, 28, 27216],
  [0, 2, 15, 59984],
  [0, 2, 4, 27424],
  [4, 1, 24, 43872],
  [0, 2, 12, 43744],
  [0, 2, 2, 37600],
  [3, 1, 21, 51568],
  [0, 2, 9, 51552],
  [7, 1, 29, 54440],
  [0, 2, 17, 54432],
  [0, 2, 5, 55888],
  [5, 1, 26, 23208],
  [0, 2, 14, 22176],
  [0, 2, 3, 42704],
  [4, 1, 23, 21224],
  [0, 2, 11, 21200],
  [8, 1, 31, 43352],
  [0, 2, 19, 43344],
  [0, 2, 7, 46240],
  [6, 1, 27, 46416],
  [0, 2, 15, 44368],
  [0, 2, 5, 21920],
  [4, 1, 24, 42448],
  [0, 2, 12, 42416],
  [0, 2, 2, 21168],
  [3, 1, 22, 43320],
  [0, 2, 9, 26928],
  [7, 1, 29, 29336],
  [0, 2, 17, 27296],
  [0, 2, 6, 44368],
  [5, 1, 26, 19880],
  [0, 2, 14, 19296],
  [0, 2, 3, 42352],
  [4, 1, 24, 21104],
  [0, 2, 10, 53856],
  [8, 1, 30, 59696],
  [0, 2, 18, 54560],
  [0, 2, 7, 55968],
  [6, 1, 27, 27472],
  [0, 2, 15, 22224],
  [0, 2, 5, 19168],
  [4, 1, 25, 42216],
  [0, 2, 12, 42192],
  [0, 2, 1, 53584],
  [2, 1, 21, 55592],
  [0, 2, 9, 54560],
];

/**
 * 二十四节气数据，节气点时间（单位是分钟）
 * 从0小寒起算
 */
const termInfo = [
  0, 21208, 42467, 63836, 85337, 107014, 128867, 150921, 173149, 195551, 218072,
  240693, 263343, 285989, 308563, 331033, 353350, 375494, 397447, 419210,
  440795, 462224, 483532, 504758,
];

/**
 * 判断农历年闰月数
 * @param {Number} year 农历年
 * return 闰月数 （月份从1开始）
 */
function getLunarLeapYear(year) {
  const yearData = lunarInfo[year - minYear];
  return yearData[0];
}

/**
 * 获取农历年份一年的每月的天数及一年的总天数
 * @param {Number} year 农历年
 */
function getLunarYearDays(year) {
  const yearData = lunarInfo[year - minYear];
  const leapMonth = yearData[0]; //闰月
  const monthData = yearData[3].toString(2);
  const monthDataArr = monthData.split("").map((item) => parseInt(item));

  //还原数据至16位,少于16位的在前面插入0（二进制存储时前面的0被忽略）
  for (let i = 0; i < 16 - monthDataArr.length; i++) {
    monthDataArr.unshift(0);
  }

  const len = leapMonth ? 13 : 12; //该年有几个月
  let yearDays = 0;
  const monthDays = [];
  for (let i = 0; i < len; i++) {
    if (monthDataArr[i] === 0) {
      yearDays += 29;
      monthDays.push(29);
    } else {
      yearDays += 30;
      monthDays.push(30);
    }
  }

  return {
    yearDays: yearDays,
    monthDays: monthDays,
  };
}

/**
 * 通过间隔天数查找农历日期
 * @param {Number} year,between 农历年，间隔天数
 * @param between
 */
function getLunarDateByBetween(year, between) {
  const lunarYearDays = getLunarYearDays(year);
  const end =
    between > 0 ? between : lunarYearDays.yearDays - Math.abs(between);
  const monthDays = lunarYearDays.monthDays;
  let tempDays = 0;
  let month = 0;
  for (let i = 0; i < monthDays.length; i++) {
    tempDays += monthDays[i];
    if (tempDays > end) {
      month = i;
      tempDays = tempDays - monthDays[i];
      break;
    }
  }

  return [year, month, end - tempDays + 1];
}

/**
 * 根据距离正月初一的天数计算农历日期
 * @param {Number} year 公历年，月，日
 * @param month
 * @param day
 */
function getLunarByBetween(year, month, day) {
  const yearData = lunarInfo[year - minYear];
  const zenMonth = yearData[1];
  const zenDay = yearData[2];
  const between = getDaysBetweenSolar(
    year,
    zenMonth - 1,
    zenDay,
    year,
    month,
    day
  );
  if (between === 0) {
    //正月初一
    return [year, 0, 1];
  } else {
    const lunarYear = between > 0 ? year : year - 1;
    return getLunarDateByBetween(lunarYear, between);
  }
}

/**
 * 两个公历日期之间的天数
 */
function getDaysBetweenSolar(year, month, day, year1, month1, day1) {
  const date = new Date(year, month, day).getTime();
  const date1 = new Date(year1, month1, day1).getTime();
  return (date1 - date) / 86400000;
}

/**
 * 计算农历日期离正月初一有多少天
 * @param {Number} year,month,day 农年，月(0-12，有闰月)，日
 * @param month
 * @param day
 */
function getDaysBetweenZheng(year, month, day) {
  const lunarYearDays = getLunarYearDays(year);
  const monthDays = lunarYearDays.monthDays;
  let days = 0;
  for (let i = 0; i < monthDays.length; i++) {
    if (i < month) {
      days += monthDays[i];
    } else {
      break;
    }
  }

  return days + day - 1;
}

/**
 * 某年的第n个节气为几日
 * 31556925974.7为地球公转周期，是毫秒
 * 1890年的正小寒点：01-05 16:02:31，1890年为基准点
 * @param {Number} y 公历年
 * @param {Number} n 第几个节气，从0小寒起算
 * 由于农历24节气交节时刻采用近似算法，可能存在少量误差(30分钟内)
 */
function getTerm(y, n) {
  const offDate = new Date(
    31556925974.7 * (y - 1890) +
      termInfo[n] * 60000 +
      Date.UTC(1890, 0, 5, 16, 2, 31)
  );
  return offDate.getUTCDate();
}

/**
 * 获取公历年一年的二十四节气
 * 返回key:日期，value:节气中文名
 */
function getYearTerm(year) {
  const res = {};
  let month = 0;
  for (let i = 0; i < 24; i++) {
    const day = getTerm(year, i);
    if (i % 2 === 0) month++;
    res[formatDayD4(month - 1, day)] = DATA.solarTerm[i];
  }
  return res;
}

/**
 * 获取生肖
 * @param {Number} year 干支所在年（默认以立春前的公历年作为基数）
 */
function getYearZodiac(year) {
  const num = year - 1890 + 25; //参考干支纪年的计算，生肖对应地支
  return DATA.zodiac[num % 12];
}

/**
 * 计算天干地支
 * @param {Number} num 60进制中的位置(把60个天干地支，当成一个60进制的数)
 */
function cyclical(num) {
  return DATA.heavenlyStems[num % 10] + DATA.earthlyBranches[num % 12];
}

/**
 * 获取干支纪年
 * @param {Number} year 干支所在年
 * @param {Number} offset 偏移量，默认为0，便于查询一个年跨两个干支纪年（以立春为分界线）
 */
function getLunarYearName(year, offset) {
  offset = offset || 0;
  //1890年1月小寒（小寒一般是1月5或6日）以前为己丑年，在60进制中排25
  return cyclical(year - 1890 + 25 + offset);
}

/**
 * 获取干支纪月
 * @param {Number} year,month 公历年，干支所在月
 * @param month
 * @param {Number} offset 偏移量，默认为0，便于查询一个月跨两个干支纪月（有立春的2月）
 */
function getLunarMonthName(year, month, offset) {
  offset = offset || 0;
  //1890年1月小寒以前为丙子月，在60进制中排12
  return cyclical((year - 1890) * 12 + month + 12 + offset);
}

/**
 * 获取干支纪日
 * @param {Number} year,month,day 公历年，月，日
 * @param month
 * @param day
 */
function getLunarDayName(year, month, day) {
  //当日与1890/1/1 相差天数
  //1890/1/1与 1970/1/1 相差29219日, 1890/1/1 日柱为壬午日(60进制18)
  const dayCyclical = Date.UTC(year, month, day) / 86400000 + 29219 + 18;
  return cyclical(dayCyclical);
}

/**
 * 获取公历月份的天数
 * @param {Number} year 公历年
 * @param {Number} month 公历月
 */
function getSolarMonthDays(year, month) {
  const monthDays = [
    31,
    isLeapYear(year) ? 29 : 28,
    31,
    30,
    31,
    30,
    31,
    31,
    30,
    31,
    30,
    31,
  ];
  return monthDays[month];
}

/**
 * 判断公历年是否是闰年
 * @param {Number} year 公历年
 */
function isLeapYear(year) {
  return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
}

/*
 * 统一日期输入参数（输入月份从1开始，内部月份统一从0开始）
 */
function formatDate(year, month, day, _minYear) {
  const argsLen = arguments.length;
  const now = fixedDate.getDate();
  year = argsLen ? parseInt(year, 10) : now.getFullYear();
  month = argsLen ? parseInt(month - 1, 10) : now.getMonth();
  day = argsLen ? parseInt(day, 10) || now.getDate() : now.getDate();
  if (year < (_minYear ? _minYear : minYear + 1) || year > maxYear)
    return { error: 100, msg: errorCode[100] };
  return {
    year: year,
    month: month,
    day: day,
  };
}

/**
 * 将农历转换为公历
 * @param {Number} _year,_month,_day 农历年，月(1-13，有闰月)，日
 * @param _month
 * @param _day
 */
function lunarToSolar(_year, _month, _day) {
  const inputDate = formatDate(_year, _month, _day);
  if (inputDate.error) return inputDate;
  const year = inputDate.year;
  const month = inputDate.month;
  const day = inputDate.day;

  const between = getDaysBetweenZheng(year, month, day); //离正月初一的天数
  const yearData = lunarInfo[year - minYear];
  const zenMonth = yearData[1];
  const zenDay = yearData[2];

  let offDate =
    new Date(year, zenMonth - 1, zenDay).getTime() + between * 86400000;
  offDate = new Date(offDate);
  return {
    year: offDate.getFullYear(),
    month: offDate.getMonth() + 1,
    day: offDate.getDate(),
  };
}

/**
 * 将公历转换为农历
 * @param {Number} _year,_month,_day 公历年，月，日
 * @param _month
 * @param _day
 */
function solarToLunar(_year, _month, _day) {
  const inputDate = formatDate(_year, _month, _day, minYear);
  if (inputDate.error) return inputDate;
  const year = inputDate.year;
  const month = inputDate.month;
  const day = inputDate.day;

  cacheUtil.setCurrent(year);
  //立春日期
  const term2 = cacheUtil.get("term2")
    ? cacheUtil.get("term2")
    : cacheUtil.set("term2", getTerm(year, 2));
  //二十四节气
  const termList = cacheUtil.get("termList")
    ? cacheUtil.get("termList")
    : cacheUtil.set("termList", getYearTerm(year));

  const firstTerm = getTerm(year, month * 2); //某月第一个节气开始日期
  const GanZhiYear =
    month > 1 || (month === 1 && day >= term2) ? year + 1 : year; //干支所在年份
  const GanZhiMonth = day >= firstTerm ? month + 1 : month; //干支所在月份（以节气为界）

  const lunarDate = getLunarByBetween(year, month, day);
  const lunarLeapMonth = getLunarLeapYear(lunarDate[0]);
  let lunarMonthName;
  if (lunarLeapMonth > 0 && lunarLeapMonth === lunarDate[1]) {
    lunarMonthName = "闰" + DATA.monthCn[lunarDate[1] - 1] + "月";
  } else if (lunarLeapMonth > 0 && lunarDate[1] > lunarLeapMonth) {
    lunarMonthName = DATA.monthCn[lunarDate[1] - 1] + "月";
  } else {
    lunarMonthName = DATA.monthCn[lunarDate[1]] + "月";
  }

  //农历节日判断
  let lunarFtv;
  const lunarMonthDays = getLunarYearDays(lunarDate[0]).monthDays;
  //除夕
  if (
    lunarDate[1] === lunarMonthDays.length - 1 &&
    lunarDate[2] === lunarMonthDays[lunarMonthDays.length - 1]
  ) {
    lunarFtv = lunarFestival["d0100"];
  } else if (lunarLeapMonth > 0 && lunarDate[1] > lunarLeapMonth) {
    lunarFtv = lunarFestival[formatDayD4(lunarDate[1] - 1, lunarDate[2])];
  } else {
    lunarFtv = lunarFestival[formatDayD4(lunarDate[1], lunarDate[2])];
  }

  return {
    zodiac: getYearZodiac(GanZhiYear),
    GanZhiYear: getLunarYearName(GanZhiYear),
    GanZhiMonth: getLunarMonthName(year, GanZhiMonth),
    GanZhiDay: getLunarDayName(year, month, day),
    //放假安排：0无特殊安排，1工作，2放假
    worktime:
      worktime["y" + year] && worktime["y" + year][formatDayD4(month, day)]
        ? worktime["y" + year][formatDayD4(month, day)]
        : 0,
    term: termList[formatDayD4(month, day)],

    lunarYear: lunarDate[0],
    lunarMonth: lunarDate[1] + 1,
    lunarDay: lunarDate[2],
    lunarMonthName: lunarMonthName,
    lunarDayName: DATA.dateCn[lunarDate[2] - 1],
    lunarLeapMonth: lunarLeapMonth,

    solarFestival: solarFestival[formatDayD4(month, day)],
    lunarFestival: lunarFtv,

    // 是否是大月
    isBigMonth: lunarMonthDays[lunarDate[1]] === 30,
  };
}

/**
 * 获取指定公历月份的农历数据
 * return res{Object}
 * @param {Number} _year,month 公历年，月
 * @param _month
 * @param {Boolean} fill 是否用上下月数据补齐首尾空缺，首例数据从周日开始
 */
function calendar(_year, _month, fill) {
  const inputDate = formatDate(_year, _month);
  if (inputDate.error) return inputDate;
  const year = inputDate.year;
  const month = inputDate.month;

  const calendarData = solarCalendar(year, month + 1, fill);
  for (let i = 0; i < calendarData.monthData.length; i++) {
    const cData = calendarData.monthData[i];
    const lunarData = solarToLunar(cData.year, cData.month, cData.day);
    extend(calendarData.monthData[i], lunarData);
  }
  return calendarData;
}

/**
 * 公历某月日历
 * return res{Object}
 * @param {Number} _year,month 公历年，月
 * @param _month
 * @param {Boolean} fill 是否用上下月数据补齐首尾空缺，首例数据从周日开始 (7*6阵列)
 */
function solarCalendar(_year, _month, fill) {
  const inputDate = formatDate(_year, _month);
  if (inputDate.error) return inputDate;
  const year = inputDate.year;
  const month = inputDate.month;

  const firstDate = new Date(year, month, 1);
  let preMonthDays, preMonthData, nextMonthData;

  const res = {
    firstDay: firstDate.getDay(), //该月1号星期几
    monthDays: getSolarMonthDays(year, month), //该月天数
    monthData: [],
  };

  res.monthData = creatLenArr(year, month + 1, res.monthDays, 1);

  if (fill) {
    if (res.firstDay > 0) {
      //前补
      const preYear = month - 1 < 0 ? year - 1 : year;
      const preMonth = month - 1 < 0 ? 11 : month - 1;
      preMonthDays = getSolarMonthDays(preYear, preMonth);
      preMonthData = creatLenArr(
        preYear,
        preMonth + 1,
        res.firstDay,
        preMonthDays - res.firstDay + 1
      );
      res.monthData = preMonthData.concat(res.monthData);
    }

    if (7 * 6 - res.monthData.length !== 0) {
      //后补
      const nextYear = month + 1 > 11 ? year + 1 : year;
      const nextMonth = month + 1 > 11 ? 0 : month + 1;
      const fillLen = 7 * 6 - res.monthData.length;
      nextMonthData = creatLenArr(nextYear, nextMonth + 1, fillLen, 1);
      res.monthData = res.monthData.concat(nextMonthData);
    }
  }

  return res;
}

/**
 * 设置放假安排【对外暴露接口】
 * @param {Object} workData
 */
function setWorktime(workData) {
  extend(worktime, workData);
}

export const LunarCalendar = {
  solarToLunar: solarToLunar,
  lunarToSolar: lunarToSolar,
  calendar: calendar,
  solarCalendar: solarCalendar,
  setWorktime: setWorktime,
  getSolarMonthDays: getSolarMonthDays,
};

export default LunarCalendar;
